#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>

// Find a better way to do this than making the file descriptor global.
int serv_fd;
void signal_callback_handler(int signum);
void handle_client(int client_fd);

int main(int argc, char *argv[]) {

    int client_fd;
    char *ip;
    short port;
    socklen_t client_addr_len;
    struct sockaddr_in serv_addr, client_addr; // server address
    
    // Register signal handler
    signal(SIGINT, signal_callback_handler);

    // Get command line arguments
    if (argc != 3) {
        printf("USAGE: %s <ip> <port>\n", argv[0]);
        exit(1);
    }
    ip = argv[1];
    port = atoi(argv[2]);

    // Open a socket
    serv_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (serv_fd < 0) {
        printf("socket() failed");
        exit(1);
    }

    // Construct our server's address
    memset(&serv_addr, 0, sizeof(serv_addr)); // Set all memory values to zero
    serv_addr.sin_family = AF_INET; // IPv4 address family
    serv_addr.sin_port = htons(port); // Change the port to network byte order
    if (inet_pton(AF_INET, ip, &serv_addr.sin_addr.s_addr) < 0) { // Convert the ip address
        printf("inet_pton() failed\n");
        exit(1);
    }
    // Bind this socket to the local address
    if (bind(serv_fd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) {
        printf("bind() failed\n");
        exit(1);
    }
    // Listen on this socket, with a max of 5 incoming connections.
    listen(serv_fd, 5);

    for(;;) { // Run until an interrupt signal is received

        // Wait for a connection
        client_addr_len = sizeof(client_addr); // Initialize with size of the structrue to hold the address
        if ((client_fd = accept(serv_fd, (struct sockaddr*) &client_addr, &client_addr_len)) < 0) {
            printf("accept() failed\n");
            exit(1);
        }
        handle_client(client_fd);
        close(client_fd);
    }

    return 0;
}

void signal_callback_handler(int signum) {
    // Clean up
    close(serv_fd);
    printf("Progam exiting\n");
    exit (signum);
}

void handle_client(int client_fd) {
    int i;
    char *pt;
    char buffer[1001];
    char loop[6];

    // Last byte of loop always stays 0
    memset(loop, 0, 6);
    // Last byte of buffer always stays 0
    memset(buffer, 0, 1001);
    // Read in from stream, 1000 bytes at a time
    while (recv(client_fd, buffer, 1000, 0)) {
        // Record the first three characters of the buffer and check
        // for the termination sequence
	loop[2] = buffer[0];
        loop[3] = buffer[1];
        loop[4] = buffer[2];
        if (strstr(loop, "\n\n\r") != NULL) {
            // Found termination sequence
            break;
        }
        // Keep track of the last two characters read in
        // in case the terminating sequence is split between
        // separate recv calls
        loop[0] = buffer[998];
        loop[1] = buffer[999];
        // Search for the terminating string
        pt = strstr(buffer, "\n\n\r");
        // Change all characters in the lowercase string to uppercase
        for (i = 0; i < strlen(buffer); ++i) {
            buffer[i] = ('A' - 'a' + buffer[i]);
        }
        if (pt != NULL) {
            // String was found!
            *pt = 0; // Terminate the string here
            printf("%s", buffer);
            fflush(stdout);
            // Found termination sequence
            break;
        } else {
            // String not found yet
            printf("%s", buffer);
            fflush(stdout);
        }
    }
}
